import math
import random
import time
import requests
import re
import os
import json
from urllib.parse import unquote, urlparse, urlencode

from DownByChrom import fetch_video_info
from X_Bogus import get_xb


class DouyinDownload:
    USER_URL = 'https://www.douyin.com/user/'
    VIDEO_URL = 'https://www.douyin.com/video/'
    NOTE_URL = 'https://www.douyin.com/note/'

    def __init__(self, share_text=None):
        self.sec_user_id = None
        self.aweme_count = None
        self.share_link_desc = share_text
        self.share_link = None
        self.video_id = None
        self.out_path = 'out'
        self.filenames = []
        with open('res/config.json', 'r') as fp:
            self.headers = json.load(fp)

    def decode_text(self) -> str:
        pattern = re.compile('(https?://(v\.douyin\.com|www\.douyin\.com/(user|video|note))/[\w\-_]+/?)')
        r = pattern.search(self.share_link_desc)
        if r == None:
            raise Exception("文本中不含有url")
        return r.group()

    def clean_filename(self, filename):
        # 将不允许的字符替换为空格
        cleaned_file_name = re.sub(r'[<>:"/\\|?*]', ' ', filename)
        # 去除文件名中的连续空格
        cleaned_file_name = re.sub(r'\s+', ' ', cleaned_file_name)
        # 去除文件名中的前后空格
        cleaned_file_name = cleaned_file_name.strip()
        return cleaned_file_name

    def get_page_url(self, url: str):
        session = requests.session()
        resp = session.get(url, headers=self.headers, allow_redirects=False)
        if not resp.ok:
            raise Exception(f'{url}无法访问: {resp.status_code}')
        self.headers['referer'] = url
        # 重定向的链接1
        url = resp.headers['location']
        print(url)
        resp = session.get(url, headers=self.headers, allow_redirects=False)
        # 重定向的链接2
        url = resp.headers['location']
        print(url)
        return url

    def prepare(self, url):
        if url.count('?') > 0:
            url = url.split('?')[0]
        self.headers['referer'] = 'https://www.iesdouyin.com/'
        data = fetch_video_info(url, self.headers)
        # out_path = 'out'
        if not os.path.exists(self.out_path):
            os.makedirs(self.out_path)
        return data

    def download_video(self, url):
        return self.prepare(url)

    def _download_video(self, aweme, out_path):
        '''
        下载视频
        :param aweme: 视频相关信息
        :param out_path: 下载路径
        :return:
        '''
        video_info = aweme['video']
        author_info = aweme['authorInfo'] if aweme.get('authorInfo') else aweme['author']
        video_desc = f"{aweme['desc']}({author_info['nickname']})"
        video_desc = self.clean_filename(video_desc)
        video_urls = video_info['playAddr'] if video_info.get('playAddr') else video_info['play_addr']['url_list']
        print(video_urls)
        # print(video_info['playApi'])
        filename = os.path.join(out_path, f"{video_desc}.mp4")
        self.filenames.append(f"{video_desc}.mp4")
        self.download(random.choice(video_urls), filename)

    def download_note(self, url):
        data = self.prepare(url)
        keys = list(data.keys())  # index = 1
        try:
            key = list(filter(lambda x: isinstance(data[x], dict) and data[x].get('aweme'), keys))[0]
        except:
            return None
        aweme = data[key]['aweme']['detail']
        del data
        self.filenames.append(self.out_path)
        self._download_note(aweme, self.out_path)

    def _download_note(self, aweme, out_path):
        music_info = aweme['music']
        image_info = aweme['images']
        author_info = aweme['authorInfo'] if aweme.get('authorInfo') else aweme['author']
        # 图片
        image_desc = f"{aweme['desc']}({author_info['nickname']})"
        image_desc = self.clean_filename(image_desc)
        out_path = os.path.join(out_path, image_desc)
        if not os.path.exists(out_path):
            os.makedirs(out_path)
        # self.filenames.append(os.path.abspath(out_path))
        # 背景音乐
        music_author = music_info["author"]
        music_title = music_info["title"]
        music_desc = f"{music_title}({music_author})"
        music_desc = self.clean_filename(music_desc)

        # 下载音乐
        music_urls = music_info['playUrl']['urlList'] if music_info.get('playUrl') else music_info['play_url'][
            'url_list']

        print(music_urls)
        if music_urls:
            filename = os.path.join(out_path, f"{music_desc}.mp3")
            self.download(random.choice(music_urls), filename)
            self.filenames.append(f"{music_desc}.mp3")
        # 下载图片
        for index, img in enumerate(image_info, 1):
            img_url = random.choice(img['urlList']) if img.get('urlList') else random.choice(img['url_list'])
            filename = os.path.join(out_path, f"{image_desc}-{index}.png")
            # index += 1
            self.download(img_url, filename)
        self.filenames.append(image_desc + '图集')

    def download_user(self, url):
        '''
        批量下载所有作品
        :param url: 分享链接
        :return: None
        '''
        data = self.prepare(url)
        keys = list(data.keys())  # index = 1
        try:
            key = list(filter(lambda x: isinstance(data[x], dict) and data[x].get('post'), keys))[0]
        except:
            return None
        aweme = data[key]
        del data
        user = aweme['user']
        post = aweme['post']
        del aweme
        max_cursor = post['maxCursor']  # 作品发布时间（时间戳）
        user_info = user['user']
        video_list = post['data']
        print(f"下载{user_info['nickname']}全部作品")
        self.sec_user_id = user_info['secUid']
        self.aweme_count = user_info['awemeCount']
        out_path = os.path.join(self.out_path, user_info['nickname'])
        if not os.path.exists(out_path):
            os.makedirs(out_path)
        self.filenames.append(os.path.abspath(out_path))
        self.headers['referer'] = url
        for video in video_list:
            # filename = f'{video["desc"]}({user_info["nickname"]}).mp4'
            # filename = os.path.join(out_path, filename)
            if video['awemeType'] == 0:
                self._download_video(video, out_path)
            elif video['awemeType'] == 68:
                self._download_note(video, out_path)
            print(video['desc'])
            # self.download(f"https:{video['video']['playApi']}", filename)
        while True:
            data = self.get_user_list(url, max_cursor)
            video_list = data['aweme_list']
            max_cursor = data['max_cursor']
            for video in video_list:
                if video['aweme_type'] == 0:
                    self._download_video(video, out_path)
                elif video['aweme_type'] == 68:
                    self._download_note(video, out_path)
                # self._download_video(video, out_path)
                print(video['desc'])
            # time.sleep(1+random.random())
            if not data['has_more']:
                break
        self.filenames = self.filenames[:10]
        self.filenames.append('...........')

    def download(self, url, filename):
        '''
        下载接口
        :param url: 下载链接
        :param filename: 文件名
        :return:
        '''
        if os.path.exists(filename):
            return
        if isinstance(url, dict):
            url = url['src']

        # self.filenames.append(os.path.basename(filename))
        if not url.startswith('http'):
            url = 'https:' + url
        # self.headers['referer'] = url
        resp = requests.get(url, headers=self.headers)
        while not resp.ok:
            time.sleep(1 + random.random())
            resp = requests.get(url, headers=self.headers)
            # raise Exception(f'{url}无法访问')

        with open(filename, 'wb') as fp:
            fp.write(resp.content)

    # 通过 url 下载视频，返回视频描述 和 视频文件地址
    def download_video_from_url(self, share_link):
        if share_link.startswith('https://v.douyin.com'):
            url = self.get_page_url(share_link)
        else:
            url = self.share_link
        if url is None:
            return
        if url.startswith(self.VIDEO_URL):
            return self.download_video(url)

    def download_share(self):
        self.filenames = []
        self.sec_user_id = None
        try:
            self.share_link = self.decode_text()
        except:
            return
        if self.share_link.startswith('https://v.douyin.com'):
            url = self.get_page_url(self.share_link)
        else:
            url = self.share_link
        if url is None:
            self.filenames = []
            return
        if url.startswith(self.VIDEO_URL):
            return self.download_video(url)
        elif url.startswith(self.NOTE_URL):
            return self.download_note(url)
        else:
            return self.download_user(url)

    def get_user_list(self, referer, max_cursor=''):
        payload = {
            'device_platform': 'webapp',
            'aid': '6383',
            'channel': 'channel_pc_web',
            'sec_user_id': self.sec_user_id,
            'max_cursor': max_cursor,
            'locate_query': 'false',
            'show_live_replay_strategy': '1',
            'count': '10',
            'publish_video_strategy_type': '2',
            'pc_client_type': '1',
            'version_code': '170400',
            'version_name': '17.4.0',
            'cookie_enabled': 'true',
            'screen_width': '1920',
            'screen_height': '1080',
            'browser_language': 'zh-CN',
            'browser_platform': 'Win32',
            'browser_name': 'Chrome',
            'browser_version': '109.0.0.0',
            'browser_online': 'true',
            'engine_name': 'Blink',
            'engine_version': '109.0.0.0',
            'os_name': 'Windows',
            'os_version': '10',
            'cpu_core_num': '8',
            'device_memory': '8',
            'platform': 'PC',
            'downlink': '10',
            'effective_type': '4g',
            'round_trip_time': '50'
        }
        query_str = urlencode(payload)
        xbogus = get_xb(query_str, self.headers['user-agent'])
        payload['X-Bogus'] = xbogus
        url = 'https://www.douyin.com/aweme/v1/web/aweme/post/'
        resp = requests.get(url, headers=self.headers, params=payload)
        # print(resp.json())
        return resp.json()


if __name__ == '__main__':
    # share_text = '1.76 DUy:/ # 日系 # 神兵小将# 清唱# jk制服# 甜妹  https://v.douyin.com/BqJQj5u/ 复制此链接，打开Dou音搜索，直接观看视频！'
    # share_link_desc = '3.07 NJv:/ 情人节染个新发色 换个心情  https://v.douyin.com/BqL4fjW/ 复制此链接，打开Dou音搜索，直接观看视频！'
    share_link_desc = '长按复制此条消息，打开抖音搜索，查看TA的更多作品。 https://v.douyin.com/BGYANYA/'
    # '长按复制此条消息，打开抖音搜索，查看TA的更多作品。 https://v.douyin.com/BGmPtFp/'
    # with open('config.json', 'r') as fp:
    #     headers = json.load(fp)
    # if len(sys.argv) > 1:
    #     share_link_desc = sys.argv[1]
    douyin = DouyinDownload()
    douyin.share_link_desc = share_link_desc
    douyin.decode_text()
    # douyin.download_share()
    douyin.sec_user_id = 'MS4wLjABAAAAuNe_oOM44eGkmFV441Oc79DulSZaOziTyDNskQU3VnQ'
    douyin.get_user_list('https://www.douyin.com/user/MS4wLjABAAAAuNe_oOM44eGkmFV441Oc79DulSZaOziTyDNskQU3VnQ')
